<template>
  <div class="appointment-calendar">
    <div class="page-header">
      <h1 class="page-title">预约日历 / 会话</h1>
    </div>

    <div v-if="!isDoctor" class="placeholder-content">
      <el-empty description="仅医生账号可使用聊天功能" />
    </div>

    <div v-else class="chat-container" v-loading="loading">
      <div class="chat-header">
        <div class="peer">
          <img class="avatar" :src="peer.avatar || defaultAvatar" @error="onAvatarError" />
          <span class="name">{{ peer.name || '会话' }}</span>
          <span v-if="peerTyping" class="typing">对方正在输入...</span>
        </div>
        <div class="actions">
          <input ref="fileInputRef" type="file" accept="image/*" style="display:none" @change="onPickImage" />
          <el-button size="big" text @click="triggerPickImage">图片</el-button>
          <el-button size="big" type="warning" text @click="startVoiceCall">
            <el-icon :size="16"><Phone /></el-icon>
            语音通话
          </el-button>
          <el-button size="big" type="success" text @click="startVideoCall">
            <el-icon :size="16"><VideoCamera /></el-icon>
            视频通话
          </el-button>
          <el-button size="big" type="primary" text @click="reload">刷新</el-button>
        </div>
      </div>

      <div class="chat-content" ref="listRef">
        <div v-for="m in messages" :key="m._id || m.createdAt" class="message" :class="{ self: isSelf(m) }">
          <div class="msg-row">
            <img class="msg-avatar" :src="getUser(m.senderId).avatar || defaultAvatar" @error="onAvatarError" />
            <div class="msg-main">
              <div class="msg-name">{{ getUser(m.senderId).name }}</div>
              <div class="bubble">
                <template v-if="m.type === 'image'">
                  <img class="image" :src="fullUrl(m.imageUrl)" />
                </template>
                <template v-else-if="m.type === 'voice'">
                  <div class="voice-box">
                    <audio :src="fullUrl(m.voiceUrl)" controls preload="none" />
                    <a class="voice-download" :href="fullUrl(m.voiceUrl)" target="_blank">下载</a>
                    <span class="voice-duration" v-if="m.voiceDuration">{{ Math.round(m.voiceDuration) }}s</span>
                  </div>
                </template>
                <template v-else>
                  <span>{{ m.content }}</span>
                </template>
              </div>
              <div class="time">{{ formatMsgTime(m.createdAt) }}</div>
            </div>
          </div>
        </div>
      </div>

      <div class="chat-input">
        <el-input v-model="input" placeholder="输入消息..." @keydown.enter="send" @input="onInputChange" />
        <el-button type="primary" @click="send">发送</el-button>
      </div>
    </div>
    
    <!-- 视频通话界面 -->
    <div v-if="isVideoCallActive" class="video-call-overlay">
      <div class="video-call-container">
        <div class="video-call-header">
          <h3>视频通话中 - {{ peer.name || '患者' }}</h3>
          <el-button type="danger" @click="endVideoCall">结束通话</el-button>
        </div>
        <div class="video-call-content">
          <div class="video-wrapper">
            <video ref="remoteVideoRef" autoplay playsinline class="remote-video"></video>
            <video ref="localVideoRef" autoplay playsinline muted class="local-video"></video>
          </div>
        </div>
      </div>
    </div>
    
    <!-- 语音通话界面 -->
    <div v-if="isVoiceCallActive" class="voice-call-overlay">
      <div class="voice-call-container">
        <div class="voice-call-header">
          <h3>语音通话中 - {{ peer.name || '患者' }}</h3>
          <el-button type="danger" @click="endVoiceCall">结束通话</el-button>
        </div>
        <div class="voice-call-content">
          <div class="voice-call-icon">
            <el-icon :size="80" color="#409eff">
              <Phone />
            </el-icon>
          </div>
          <div class="voice-call-status">
            <p>语音通话已连接</p>
            <p class="voice-call-tip">请确保麦克风权限已开启</p>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref, computed, nextTick } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { VideoCamera, Phone } from '@element-plus/icons-vue'
import { useUserStore } from '@/stores/user'
import { io as createIO, Socket } from 'socket.io-client'

const route = useRoute()
const userStore = useUserStore()

const isDoctor = computed(() => userStore.userInfo?.roles?.some(r => r.code === 'doctor'))

const loading = ref(false)
const listRef = ref<HTMLDivElement | null>(null)
const fileInputRef = ref<HTMLInputElement | null>(null)
const input = ref('')
const messages = ref<any[]>([])
const chatId = ref<string | null>(null)
const socketRef = ref<Socket | null>(null)
const peerTyping = ref(false)
let typingTimer: any = null

const peer = ref<{ id?: string, name?: string, avatar?: string }>({})

// 视频通话相关状态
const isVideoCallActive = ref(false)
const videoCallId = ref<string | null>(null)
const localVideoRef = ref<HTMLVideoElement | null>(null)
const remoteVideoRef = ref<HTMLVideoElement | null>(null)
const peerConnection = ref<RTCPeerConnection | null>(null)
const localStream = ref<MediaStream | null>(null)

// 语音通话（音频-only）相关状态
const isVoiceCallActive = ref(false)
const voiceCallId = ref<string | null>(null)
const voicePeerConnection = ref<RTCPeerConnection | null>(null)
const voiceLocalStream = ref<MediaStream | null>(null)

const API_BASE = (import.meta as any).env?.VITE_API_BASE_URL || localStorage.getItem('API_BASE') || 'http://192.168.7.125:3000'
// 统一以IP形式作为资源基础地址（仅本组件使用，不影响其他代码）
const API_IP_BASE = (() => {
  const override = localStorage.getItem('API_IP_BASE')
  if (override) return override
  try {
    const base = new URL(API_BASE)
    let host = base.hostname
    const localHost = window.location.hostname
    // 如果当前访问主机是IP，则优先使用该IP
    if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(localHost)) host = localHost
    const port = base.port ? `:${base.port}` : ''
    return `${base.protocol}//${host}${port}`
  } catch {
    return API_BASE
  }
})()

function scrollToBottom() {
  nextTick(() => {
    if (listRef.value) listRef.value.scrollTop = listRef.value.scrollHeight
  })
}

function formatMsgTime(t: string | number | Date) {
  if (!t) return ''
  const d = new Date(t)
  return `${d.getHours().toString().padStart(2,'0')}:${d.getMinutes().toString().padStart(2,'0')}`
}

function isSelf(m: any) {
  return String(m.senderId) === String(selfId.value)
}

function fullUrl(u?: string) {
  if (!u) return ''
  // 相对路径 → 拼接
  if (!u.startsWith('http')) return `${API_IP_BASE}${u}`
  // 绝对路径但主机不是期望的，统一替换为 API_BASE，仅针对 /uploads/
  const i = u.indexOf('/uploads/')
  if (i > -1) return `${API_IP_BASE}${u.slice(i)}`
  return u
}

async function fetchJSON(url: string, options?: RequestInit): Promise<any> {
  const res = await fetch(url, options)
  if (!res.ok) throw new Error(`HTTP ${res.status}`)
  return res.json()
}

const selfId = ref<string>('')
const userMap = ref<Record<string, { name: string; avatar: string }>>({})
const defaultAvatar = 'https://picsum.photos/40'
function onAvatarError(e: Event) {
  const img = e.target as HTMLImageElement
  if (img && img.src !== defaultAvatar) img.src = defaultAvatar
}

function getUser(id?: string) {
  const key = String(id || '')
  return userMap.value[key] || { name: key ? `用户${key.slice(-4)}` : '用户', avatar: defaultAvatar }
}

// 去重追加消息，避免发送后本地push与socket回推重复
function pushUniqueMessage(m: any) {
  if (!m) return
  const mid = String(m._id || '')
  if (mid) {
    if (messages.value.some(x => String(x._id || '') === mid)) return
  } else {
    const mk = `${m.senderId}|${m.type || 'text'}|${m.content || m.imageUrl || m.voiceUrl || ''}|${m.createdAt}`
    if (messages.value.some(x => `${x.senderId}|${x.type || 'text'}|${x.content || x.imageUrl || x.voiceUrl || ''}|${x.createdAt}` === mk)) return
  }
  messages.value.push(m)
}

async function ensureUsersForMessages(list: any[]) {
  const ids = Array.from(new Set((list || []).map(m => String(m.senderId || '')).filter(Boolean)))
  if (!ids.length) return
  const normalIds = ids.filter(id => !id.startsWith('doctor_'))
  const doctorIds = ids.filter(id => id.startsWith('doctor_')).map(id => id.replace('doctor_', ''))

  try {
    // 普通用户（优先使用 wechat_users 的 avatarUrl 与 nickname）
    if (normalIds.length) {
      const res = await fetchJSON(`${API_BASE}/users/byIds?ids=${encodeURIComponent(normalIds.join(','))}`)
      const arr: any[] = Array.isArray(res) ? res : (Array.isArray(res?.list) ? res.list : (Array.isArray(res?.items) ? res.items : Object.values(res || {})))
      arr.forEach(u => {
        if (u && u._id) userMap.value[String(u._id)] = { name: u.nickname || u.username || '用户', avatar: u.avatarUrl || u.avatar || defaultAvatar }
      })
    }
    // 医生虚拟ID
    if (doctorIds.length) {
      const data = await fetchJSON(`${API_BASE}/cosmetic/doctors?limit=1000`)
      const docs: any[] = Array.isArray(data) ? data : (Array.isArray(data?.data) ? data.data : [])
      docs.forEach(d => {
        if (d && d._id) userMap.value[`doctor_${d._id}`] = { name: d.name || '医生', avatar: d.avatar || defaultAvatar }
      })
    }
  } catch (_) {}
}

async function getDoctorVirtualId(): Promise<string | null> {
  const username = userStore.userInfo?.username || ''
  if (!username) return null
  try {
    const data = await fetchJSON(`${API_BASE}/cosmetic/doctors?limit=1000`)
    const doctors = Array.isArray(data) ? data : (Array.isArray(data?.data) ? data.data : [])
    const doc = doctors.find((d: any) => d?.name === username)
    if (doc && doc._id) {
      // 同时填充自己的头像昵称
      userMap.value[`doctor_${doc._id}`] = { name: doc.name || username, avatar: doc.avatar || defaultAvatar }
      return `doctor_${doc._id}`
    }
    return null
  } catch (e) {
    return null
  }
}

function connectSocket(userId: string) {
  try {
    console.log('PC端连接Socket，用户ID:', userId)
    const s = createIO(API_BASE, { query: { userId } })
    socketRef.value = s
    s.on('connect', () => {})
    s.on('chat:newMessage', async (payload: any) => {
      if (payload && String(payload.chatId) === String(chatId.value)) {
        pushUniqueMessage(payload.message)
        // 确保有发送者的头像昵称
        await ensureUsersForMessages([payload.message])
        const sender = String(payload.message?.senderId || '')
        if (sender && sender !== String(selfId.value)) {
          try {
            fetchJSON(`${API_BASE}/chats/${chatId.value}/readAll`, {
              method: 'POST', headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ userId: selfId.value })
            })
          } catch (_) {}
        }
        scrollToBottom()
      }
    })
    s.on('chat:typing', (p: any) => {
      if (p && String(p.chatId) === String(chatId.value)) {
        peerTyping.value = true
        if (typingTimer) clearTimeout(typingTimer)
        typingTimer = setTimeout(() => { peerTyping.value = false }, 1500)
      }
    })
    s.on('chat:readAll', (p: any) => {
      if (p && String(p.chatId) === String(chatId.value)) {
        // 可按需更新已读状态
      }
    })
    
    // 视频通话相关事件
    s.on('video:incoming', async (payload: any) => {
      console.log('PC端收到视频通话邀请:', payload)
      if (payload && payload.from && payload.callId) {
        // 使用Element Plus的确认对话框
        const { ElMessageBox } = await import('element-plus')
        try {
          await ElMessageBox.confirm(
            `${payload.fromName || '对方'} 邀请您进行视频通话，是否接听？`,
            '视频通话邀请',
            {
              confirmButtonText: '接听',
              cancelButtonText: '拒绝',
              type: 'info',
            }
          )
          // 用户点击了接听
          videoCallId.value = payload.callId
          await initWebRTC(false) // false表示是接听方
          isVideoCallActive.value = true
          ElMessage.success('正在连接视频通话...')
        } catch {
          // 用户点击了拒绝
          console.log('用户拒绝了视频通话邀请')
          ElMessage.info('已拒绝视频通话邀请')
        }
      }
    })
    
    s.on('video:offer', async (payload: any) => {
      if (payload && payload.callId === videoCallId.value && payload.offer) {
        await answerVideoCall(payload.offer)
      }
    })
    
    s.on('video:answer', async (payload: any) => {
      if (payload && payload.callId === videoCallId.value && payload.answer) {
        await peerConnection.value!.setRemoteDescription(payload.answer)
      }
    })
    
    s.on('video:ice-candidate', async (payload: any) => {
      if (payload && payload.callId === videoCallId.value && payload.candidate) {
        await peerConnection.value!.addIceCandidate(payload.candidate)
      }
    })
    
    s.on('video:end', (payload: any) => {
      if (payload && payload.callId === videoCallId.value) {
        endVideoCall()
        ElMessage.info('对方已结束视频通话')
      }
    })

    // 语音通话相关事件
    s.on('voice:incoming', async (payload: any) => {
      console.log('PC端收到语音通话邀请:', payload)
      console.log('PC端当前用户ID:', selfId.value)
      console.log('PC端Socket连接状态:', s.connected)
      if (payload && payload.from && payload.callId) {
        const { ElMessageBox } = await import('element-plus')
        try {
          await ElMessageBox.confirm(
            `${payload.fromName || '对方'} 邀请您进行语音通话，是否接听？`,
            '语音通话邀请',
            { confirmButtonText: '接听', cancelButtonText: '拒绝', type: 'info' }
          )
          voiceCallId.value = payload.callId
          await initVoiceWebRTC(false)
          isVoiceCallActive.value = true
          ElMessage.success('正在连接语音通话...')
        } catch {
          console.log('用户拒绝了语音通话邀请')
          ElMessage.info('已拒绝语音通话邀请')
          try { socketRef.value?.emit('voice:reject', { callId: payload.callId, to: payload.from }) } catch (_) {}
        }
      }
    })
    s.on('voice:offer', async (payload: any) => {
      if (payload && payload.callId === voiceCallId.value && payload.offer) {
        await answerVoiceCall(payload.offer)
      }
    })
    s.on('voice:answer', async (payload: any) => {
      if (payload && payload.callId === voiceCallId.value && payload.answer) {
        await voicePeerConnection.value!.setRemoteDescription(payload.answer)
      }
    })
    s.on('voice:ice-candidate', async (payload: any) => {
      if (payload && payload.callId === voiceCallId.value && payload.candidate) {
        await voicePeerConnection.value!.addIceCandidate(payload.candidate)
      }
    })
    s.on('voice:end', (payload: any) => {
      if (payload && payload.callId === voiceCallId.value) {
        endVoiceCall()
        ElMessage.info('对方已结束语音通话')
      }
    })
  } catch (_) {}
}

function joinRoom(cid: string) {
  try { socketRef.value?.emit('chat:join', cid) } catch (_) {}
}
function leaveRoom(cid: string) {
  try { socketRef.value?.emit('chat:leave', cid) } catch (_) {}
}

async function openOrGetChat() {
  const peerId = String(route.query.peerId || '')
  const peerName = String(route.query.peerName || '')
  if (!peerId) throw new Error('缺少会话对端（peerId）')
  peer.value = { id: peerId, name: peerName }

  const me = await getDoctorVirtualId()
  if (!me) throw new Error('未找到医生虚拟ID')
  selfId.value = me

  if (!socketRef.value) connectSocket(me)

  // 预取对端头像昵称：优先 wechat_users.avatarUrl
  try {
    if (!peerId.startsWith('doctor_')) {
      const res = await fetchJSON(`${API_BASE}/users/byIds?ids=${encodeURIComponent(peerId)}`)
      const arr: any[] = Array.isArray(res) ? res : (Array.isArray(res?.list) ? res.list : (Array.isArray(res?.items) ? res.items : Object.values(res || {})))
      const u = arr[0]
      if (u) {
        userMap.value[String(u._id)] = { name: u.nickname || u.username || '用户', avatar: u.avatarUrl || u.avatar || defaultAvatar }
        // 填充到页眉展示
        peer.value.name = peerName || u.nickname || u.username
        peer.value.avatar = u.avatarUrl || u.avatar || peer.value.avatar || defaultAvatar
      }
    }
  } catch (_) {}

  const data = await fetchJSON(`${API_BASE}/chats/open`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ a: me, b: peerId })
  })
  const id = data?._id || data?.data?._id
  if (!id) throw new Error('打开会话失败')
  chatId.value = id
  joinRoom(id)
}

async function loadMessages() {
  if (!chatId.value) return
  const list = await fetchJSON(`${API_BASE}/chats/${chatId.value}/messages`)
  messages.value = Array.isArray(list) ? list : []
  await ensureUsersForMessages(messages.value)
  // 标记为已读
  try {
    await fetchJSON(`${API_BASE}/chats/${chatId.value}/readAll`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId: selfId.value })
    })
  } catch (_) {}
  scrollToBottom()
}

async function reload() {
  try {
    loading.value = true
    if (!chatId.value) await openOrGetChat()
    await loadMessages()
  } catch (e: any) {
    ElMessage.error(e?.message || '加载失败')
  } finally {
    loading.value = false
  }
}

function emitTyping() {
  try {
    if (socketRef.value && chatId.value) {
      socketRef.value.emit('chat:typing', { chatId: chatId.value })
    }
  } catch (_) {}
}

let inputTypingDebounce: any = null
function onInputChange() {
  if (inputTypingDebounce) clearTimeout(inputTypingDebounce)
  emitTyping()
  inputTypingDebounce = setTimeout(() => {}, 500)
}

async function send() {
  const text = (input.value || '').trim()
  if (!text) return
  if (!chatId.value) {
    ElMessage.error('会话未打开')
    return
  }
  try {
    const msg = await fetchJSON(`${API_BASE}/chats/${chatId.value}/messages`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ senderId: selfId.value, content: text })
    })
    pushUniqueMessage(msg)
    await ensureUsersForMessages([msg])
    input.value = ''
    scrollToBottom()
  } catch (e: any) {
    ElMessage.error(e?.message || '发送失败')
  }
}

function triggerPickImage() {
  fileInputRef.value?.click()
}

async function onPickImage(e: Event) {
  try {
    const inputEl = e.target as HTMLInputElement
    const file = inputEl.files && inputEl.files[0]
    if (!file) return
    if (!chatId.value) return

    const form = new FormData()
    form.append('file', file)
    form.append('type', 'image')
    const ext = (file.name.split('.').pop() || '').toLowerCase()
    form.append('ext', ext)

    const res = await fetch(`${API_BASE}/upload`, { method: 'POST', body: form })
    if (!res.ok) throw new Error(`上传失败 HTTP ${res.status}`)
    const data = await res.json()

    // 统一图片URL到 IP 基础地址
    let imgUrl = data?.url as string
    if (imgUrl) {
      if (!imgUrl.startsWith('http')) imgUrl = `${API_IP_BASE}${imgUrl}`
      else {
        const i = imgUrl.indexOf('/uploads/')
        if (i > -1) imgUrl = `${API_IP_BASE}${imgUrl.slice(i)}`
      }
    }
    const msg = await fetchJSON(`${API_BASE}/chats/${chatId.value}/messages`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ senderId: selfId.value, type: 'image', imageUrl: imgUrl })
    })
    pushUniqueMessage(msg)
    await ensureUsersForMessages([msg])
    scrollToBottom()
    // 清理选择
    inputEl.value = ''
  } catch (err: any) {
    ElMessage.error(err?.message || '发送图片失败')
  }
}

onMounted(() => {
  if (!isDoctor.value) return
  reload()
})

// 语音通话相关方法
async function startVoiceCall() {
  if (!peer.value.id) {
    ElMessage.error('无法发起语音通话：缺少对方信息')
    return
  }
  
  try {
    console.log('🎤 开始获取麦克风权限...')
    
    // 获取本地音频流
    const stream = await navigator.mediaDevices.getUserMedia({ 
      audio: true, 
      video: false 
    })
    
    console.log('✅ 成功获取音频流:', {
      audioTracks: stream.getAudioTracks().length,
      stream: stream
    })
    
    voiceLocalStream.value = stream
    
    // 创建语音通话房间ID
    voiceCallId.value = `voice_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
    
    // 发送语音通话邀请
    if (socketRef.value) {
      console.log('PC端发起语音通话:', {
        to: peer.value.id,
        callId: voiceCallId.value,
        from: selfId.value,
        fromName: userStore.userInfo?.username || '医生'
      })
      
      // 发送给后端，让后端转发给目标用户
      socketRef.value.emit('voice:forward', {
        to: peer.value.id,
        callId: voiceCallId.value,
        from: selfId.value,
        fromName: userStore.userInfo?.username || '医生'
      })
    }
    
    // 初始化WebRTC连接
    await initVoiceWebRTC(true) // true表示是发起方
    
    // 激活语音通话界面
    isVoiceCallActive.value = true
    
    ElMessage.success('正在发起语音通话...')
    
  } catch (error: any) {
    console.error('❌ 启动语音通话失败:', error)
    ElMessage.error('无法访问麦克风，请检查权限设置')
    // 清理状态
    isVoiceCallActive.value = false
    if (voiceLocalStream.value) {
      voiceLocalStream.value.getTracks().forEach(track => track.stop())
      voiceLocalStream.value = null
    }
  }
}

// 视频通话相关方法
async function startVideoCall() {
  if (!peer.value.id) {
    ElMessage.error('无法发起视频通话：缺少对方信息')
    return
  }
  
  try {
    console.log('🎥 开始获取媒体设备权限...')
    
    // 获取本地媒体流
    const stream = await navigator.mediaDevices.getUserMedia({ 
      video: true, 
      audio: true 
    })
    
    console.log('✅ 成功获取媒体流:', {
      videoTracks: stream.getVideoTracks().length,
      audioTracks: stream.getAudioTracks().length,
      stream: stream
    })
    
    localStream.value = stream
    
    // 先激活视频通话界面，确保DOM元素存在
    isVideoCallActive.value = true
    
    // 等待DOM更新完成
    await nextTick()
    
    // 创建视频通话房间ID
    videoCallId.value = `video_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
    
    // 发送视频通话邀请
    if (socketRef.value) {
      console.log('PC端发起视频通话:', {
        to: peer.value.id,
        callId: videoCallId.value,
        from: selfId.value,
        fromName: userStore.userInfo?.username || '医生'
      })
      
      // 发送给后端，让后端转发给目标用户
      socketRef.value.emit('video:forward', {
        to: peer.value.id,
        callId: videoCallId.value,
        from: selfId.value,
        fromName: userStore.userInfo?.username || '医生'
      })
    }
    
    // 再次等待确保视频元素已渲染
    await nextTick()
    
    // 检查视频元素状态
    console.log('🔍 检查视频元素状态:', {
      localVideoRef: localVideoRef.value,
      remoteVideoRef: remoteVideoRef.value,
      localStream: localStream.value,
      isVideoCallActive: isVideoCallActive.value
    })
    
    if (localVideoRef.value && localStream.value) {
      console.log('📹 设置本地视频流到元素')
      localVideoRef.value.srcObject = localStream.value
      await localVideoRef.value.play()
      console.log('✅ 本地视频播放成功')
    } else {
      console.error('❌ 视频元素未找到或媒体流为空')
      ElMessage.error('视频元素初始化失败')
      return
    }
    
    // 初始化WebRTC连接
    await initWebRTC(true) // true表示是发起方
    
    ElMessage.success('正在发起视频通话...')
    
  } catch (error: any) {
    console.error('❌ 启动视频通话失败:', error)
    ElMessage.error('无法访问摄像头和麦克风，请检查权限设置')
    // 清理状态
    isVideoCallActive.value = false
    if (localStream.value) {
      localStream.value.getTracks().forEach(track => track.stop())
      localStream.value = null
    }
  }
}

async function initWebRTC(isInitiator: boolean) {
  try {
    console.log('🔧 初始化WebRTC连接...')
    
    // 创建RTCPeerConnection
    const config = {
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' },
        { urls: 'stun:stun1.l.google.com:19302' }
      ]
    }
    
    peerConnection.value = new RTCPeerConnection(config)
    console.log('✅ RTCPeerConnection 创建成功')
    
    // 添加本地流
    if (localStream.value) {
      console.log('📹 添加本地流到WebRTC连接')
      localStream.value.getTracks().forEach(track => {
        peerConnection.value!.addTrack(track, localStream.value!)
        console.log('添加轨道:', track.kind, track.label)
      })
    } else {
      console.warn('⚠️ localStream 为空')
    }
    
    // 处理远程流
    peerConnection.value.ontrack = (event) => {
      console.log('📡 收到远程流:', event)
      if (remoteVideoRef.value) {
        remoteVideoRef.value.srcObject = event.streams[0]
        remoteVideoRef.value.play().catch(e => console.error('远程视频播放失败:', e))
        console.log('✅ 远程视频流已设置')
      } else {
        console.warn('⚠️ remoteVideoRef 为空')
      }
    }
    
    // 处理ICE候选
    peerConnection.value.onicecandidate = (event) => {
      if (event.candidate && socketRef.value) {
        console.log('🧊 发送ICE候选:', event.candidate)
        socketRef.value.emit('video:ice-candidate', {
          callId: videoCallId.value,
          candidate: event.candidate,
          to: peer.value.id
        })
      }
    }
    
    // 连接状态变化处理
    peerConnection.value.onconnectionstatechange = () => {
      console.log('🔗 连接状态变化:', peerConnection.value?.connectionState)
    }
    
    // 如果是发起方，创建offer
    if (isInitiator) {
      console.log('📞 创建WebRTC offer...')
      const offer = await peerConnection.value.createOffer()
      await peerConnection.value.setLocalDescription(offer)
      console.log('✅ Offer 创建并设置成功')
      
      if (socketRef.value) {
        socketRef.value.emit('video:offer', {
          callId: videoCallId.value,
          offer: offer,
          to: peer.value.id
        })
        console.log('📤 Offer 已发送')
      }
    }
    
  } catch (error) {
    console.error('❌ WebRTC初始化失败:', error)
    ElMessage.error('视频通话初始化失败')
  }
}

async function answerVideoCall(offer: RTCSessionDescriptionInit) {
  try {
    await peerConnection.value!.setRemoteDescription(offer)
    const answer = await peerConnection.value!.createAnswer()
    await peerConnection.value!.setLocalDescription(answer)
    
    if (socketRef.value) {
      socketRef.value.emit('video:answer', {
        callId: videoCallId.value,
        answer: answer,
        to: peer.value.id
      })
    }
  } catch (error) {
    console.error('应答视频通话失败:', error)
  }
}

function endVideoCall() {
  try {
    // 停止本地流
    if (localStream.value) {
      localStream.value.getTracks().forEach(track => track.stop())
      localStream.value = null
    }
    
    // 关闭WebRTC连接
    if (peerConnection.value) {
      peerConnection.value.close()
      peerConnection.value = null
    }
    
    // 发送结束通话信号
    if (socketRef.value && videoCallId.value) {
      socketRef.value.emit('video:end', {
        callId: videoCallId.value,
        to: peer.value.id
      })
    }
    
    isVideoCallActive.value = false
    videoCallId.value = null
    
    ElMessage.success('视频通话已结束')
  } catch (error) {
    console.error('结束视频通话失败:', error)
  }
}

// 语音通话（音频-only）方法
async function initVoiceWebRTC(isInitiator: boolean) {
  try {
    const config = { iceServers: [ { urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' } ] }
    voicePeerConnection.value = new RTCPeerConnection(config)
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false })
      voiceLocalStream.value = stream
      stream.getTracks().forEach(t => voicePeerConnection.value!.addTrack(t, stream))
    } catch (e) {
      console.error('获取麦克风失败:', e)
      ElMessage.error('无法访问麦克风')
      return
    }
    voicePeerConnection.value.onicecandidate = (event) => {
      if (event.candidate && socketRef.value) {
        socketRef.value.emit('voice:ice-candidate', { callId: voiceCallId.value, candidate: event.candidate, to: peer.value.id })
      }
    }
    voicePeerConnection.value.onconnectionstatechange = () => {
      console.log('语音通话连接状态:', voicePeerConnection.value?.connectionState)
    }
    if (isInitiator) {
      const offer = await voicePeerConnection.value.createOffer({ offerToReceiveAudio: true, offerToReceiveVideo: false })
      await voicePeerConnection.value.setLocalDescription(offer)
      if (socketRef.value) {
        socketRef.value.emit('voice:offer', { callId: voiceCallId.value, offer, to: peer.value.id })
      }
    }
  } catch (error) {
    console.error('语音通话初始化失败:', error)
    ElMessage.error('语音通话初始化失败')
  }
}

async function answerVoiceCall(offer: RTCSessionDescriptionInit) {
  try {
    await voicePeerConnection.value!.setRemoteDescription(offer)
    const answer = await voicePeerConnection.value!.createAnswer()
    await voicePeerConnection.value!.setLocalDescription(answer)
    if (socketRef.value) {
      socketRef.value.emit('voice:answer', { callId: voiceCallId.value, answer, to: peer.value.id })
    }
  } catch (error) {
    console.error('应答语音通话失败:', error)
  }
}

function endVoiceCall() {
  try {
    if (voiceLocalStream.value) {
      voiceLocalStream.value.getTracks().forEach(t => t.stop())
      voiceLocalStream.value = null
    }
    if (voicePeerConnection.value) {
      voicePeerConnection.value.close()
      voicePeerConnection.value = null
    }
    if (socketRef.value && voiceCallId.value) {
      socketRef.value.emit('voice:end', { callId: voiceCallId.value, to: peer.value.id })
    }
    isVoiceCallActive.value = false
    voiceCallId.value = null
  } catch (error) {
    console.error('结束语音通话失败:', error)
  }
}


onBeforeUnmount(() => {
  if (chatId.value) leaveRoom(chatId.value)
  if (isVideoCallActive.value) endVideoCall()
  if (isVoiceCallActive.value) endVoiceCall()
  try { socketRef.value?.disconnect() } catch (_) {}
})
</script>

<style lang="scss" scoped>
.appointment-calendar {
  .page-header {
    margin-bottom: 24px;

    .page-title {
      margin: 0;
      font-size: 24px;
      font-weight: 600;
      color: #333;
    }
  }

  .placeholder-content {
    background: white;
    border-radius: 8px;
    padding: 60px;
    text-align: center;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    height: 100vh;
  }

  .chat-container {
    background: #fff;
    border-radius: 8px;
    padding: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    display: flex;
    flex-direction: column;
    height: calc(100vh - 140px);

    .chat-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 8px 4px 12px 4px;
      border-bottom: 1px solid #eee;

      .peer {
        display: flex;
        align-items: center;
        gap: 8px;
        .avatar {
          width: 28px;
          height: 28px;
          border-radius: 50%;
          object-fit: cover;
        }
        .name {
          font-weight: 600;
          color: #333;
        }
        .typing { color: #999; font-size: 12px; margin-left: 8px; }
      }
      .actions { display: flex; align-items: center; gap: 8px; }
    }

    .chat-content {
      flex: 1;
      overflow-y: auto;
      padding: 12px 4px;
      display: flex;
      flex-direction: column;
      gap: 8px;

      .message {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        max-width: 100%;

        .msg-row { display: flex; align-items: flex-start; gap: 8px; max-width: 100%; }
        .msg-avatar { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; }
        .msg-main { max-width: calc(100% - 36px); }
        .msg-name { color: #666; font-size: 12px; margin-bottom: 4px; }

        .bubble {
          display: inline-block;
          max-width: 100%;
          background: #f5f5f5;
          padding: 8px 10px;
          border-radius: 8px;
          color: #333;
          white-space: normal;
          word-break: normal;
          overflow-wrap: break-word;

          .image { max-width: 280px; max-height: 280px; width: auto; height: auto; border-radius: 6px; display: block; object-fit: contain; }
          .voice { color: #67c23a; }
          .voice-box { display: flex; align-items: center; gap: 8px; }
          .voice-box audio { height: 28px; outline: none; }
          .voice-download { font-size: 12px; color: #409eff; text-decoration: none; }
          .voice-download:hover { text-decoration: underline; }
          .voice-duration { font-size: 12px; color: #999; }
        }
        .time {
          color: #999;
          font-size: 12px;
          margin-top: 4px;
        }

        &.self {
          align-self: flex-end;
          align-items: flex-end;
          .msg-row { flex-direction: row-reverse; }
          .msg-main { text-align: right; }
          .bubble { background: #f5f5f5; }
          .time { text-align: right; }
        }
      }
    }

    .chat-input {
      display: flex;
      align-items: center;
      gap: 8px;
      border-top: 1px solid #eee;
      padding-top: 8px;
    }
  }
  
  // 视频通话样式
  .video-call-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.9);
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    
    .video-call-container {
      width: 90%;
      max-width: 800px;
      height: 80%;
      background: #fff;
      border-radius: 12px;
      overflow: hidden;
      display: flex;
      flex-direction: column;
      
      .video-call-header {
        background: #f5f5f5;
        padding: 16px 20px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        border-bottom: 1px solid #eee;
        
        h3 {
          margin: 0;
          color: #333;
          font-size: 18px;
        }
      }
      
      .video-call-content {
        flex: 1;
        position: relative;
        background: #000;
        
        .video-wrapper {
          width: 100%;
          height: 100%;
          position: relative;
          
          .remote-video {
            width: 100%;
            height: 100%;
            object-fit: cover;
          }
          
          .local-video {
            position: absolute;
            top: 20px;
            right: 20px;
            width: 200px;
            height: 150px;
            object-fit: cover;
            border-radius: 8px;
            border: 2px solid #fff;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
          }
        }
      }
    }
  }
  
  // 语音通话样式
  .voice-call-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.9);
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    
    .voice-call-container {
      width: 400px;
      height: 300px;
      background: #fff;
      border-radius: 12px;
      overflow: hidden;
      display: flex;
      flex-direction: column;
      
      .voice-call-header {
        background: #f5f5f5;
        padding: 16px 20px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        border-bottom: 1px solid #eee;
        
        h3 {
          margin: 0;
          color: #333;
          font-size: 18px;
        }
      }
      
      .voice-call-content {
        flex: 1;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 40px 20px;
        
        .voice-call-icon {
          margin-bottom: 20px;
          animation: pulse 2s infinite;
        }
        
        .voice-call-status {
          text-align: center;
          
          p {
            margin: 8px 0;
            color: #333;
            font-size: 16px;
            
            &.voice-call-tip {
              color: #666;
              font-size: 14px;
            }
          }
        }
      }
    }
  }
  
  @keyframes pulse {
    0% {
      transform: scale(1);
    }
    50% {
      transform: scale(1.1);
    }
    100% {
      transform: scale(1);
    }
  }
}
</style>